package com.lz.autotest.project.testmanage.infmanage.controller;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.NumberUtil;
import com.alibaba.fastjson2.JSONArray;
import com.lz.autotest.common.constant.ReportSourceConstant;
import com.lz.autotest.common.entity.ResponseVo;
import com.lz.autotest.common.exception.ServiceException;
import com.lz.autotest.common.utils.DateUtils;
import com.lz.autotest.common.utils.ReportExcelUtils;
import com.lz.autotest.common.utils.file.FileUtils;
import com.lz.autotest.common.utils.poi.*;
import com.lz.autotest.common.web.controller.BaseController;
import com.lz.autotest.project.testmanage.autoTest.entity.TsstAutotestMenu;
import com.lz.autotest.project.testmanage.autoTest.service.TsstAutotestMenuService;
import com.lz.autotest.project.testmanage.infmanage.domain.SstInf;
import com.lz.autotest.project.testmanage.reportmanage.domain.SstInfReport;
import com.lz.autotest.project.testmanage.reportmanage.service.InfReportService;
import com.lz.autotest.project.testmanage.infmanage.service.InfService;
import com.lz.autotest.project.testmanage.envmanage.service.TsstEnvService;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

import static java.math.BigDecimal.ROUND_HALF_UP;

/**
 * 接口信息
 *
 * @author lz
 */
@RestController
@RequestMapping("/inf")
public class InfController extends BaseController {
    private static final Logger logger = LoggerFactory.getLogger(InfController.class);

    Snowflake snowflake = IdUtil.createSnowflake(1, 1);
    @Resource
    private InfService infService;
    @Resource
    private InfReportService infReportService;

    @Resource
    private TsstAutotestMenuService txtsTestMenuService;
    @Resource
    private TsstEnvService tsstEnvService;

    /**
     * 请求
     */
    @PreAuthorize("@ss.hasPermi('testmanage:infManage:request')")
    @PostMapping("/requestServe")
    public ResponseVo requestServe(@RequestBody SstInf sstInf) {
        return infService.requestServe(sstInf);
    }

    /**
     * 批量请求
     */
    @PreAuthorize("@ss.hasPermi('testmanage:onekeyTest:run')")
    @PostMapping("/requestServeBatch")
    public ResponseVo requestServeBatch(@RequestBody SstInf sstInf) {
        TsstAutotestMenu tsstAutotestMenu = new TsstAutotestMenu();
        tsstAutotestMenu.setMenuName(sstInf.getInfName()+"_"+DateUtils.getCurr_YYYY_MM_DD_HH_MM_SS());
        tsstAutotestMenu.setRunEnv(sstInf.getInfEnv());
        tsstAutotestMenu.setCycleNumber(sstInf.getRound());
        tsstAutotestMenu.setThreadNumber(sstInf.getConcurrency());
        List<SstInf> list = new ArrayList<>();
        list.add(sstInf);
        tsstAutotestMenu.setInfList(list);
        tsstAutotestMenu.setMcs(sstInf.isMcs());
        tsstAutotestMenu.setReportSource(ReportSourceConstant.ONE_KEY_TEST);
        return txtsTestMenuService.autoTestRun(tsstAutotestMenu);
    }

    @PreAuthorize("@ss.hasPermi('testmanage:report:exportReport')")
    @PostMapping("/exportReport")
    public void exportReport(HttpServletResponse response, @RequestParam Map<String, Object> param) {

        List<SstInfReport> list = infReportService.queryAutoReportListByExport(param);
        if (list.size() == 0) {
            throw new ServiceException("没有找到对应的报告，请先进行压测！");
        }
        SstInfReport sstInfReport = list.get(0);
        exportExcel(response, sstInfReport);

    }

    @PreAuthorize("@ss.hasPermi('testmanage:report:exportReport')")
    @PostMapping("/exportAtReport")
    public void exportAtReport(HttpServletResponse response, @RequestParam Map<String, Object> param) {
        List<SstInfReport> list = infReportService.queryAutoReportListByExport(param);
        if (list.isEmpty()) {
            throw new ServiceException("没有找到对应的报告，请先进行压测！");
        }
        SstInfReport sstInfReport = list.get(0);
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        //设置文件名称
        String fileName = sstInfReport.getReportName() + ".xlsx";
        response.setHeader("Content-Disposition", "filename=" + FileUtils.toUtf8String(fileName));

        XSSFWorkbook wb = new XSSFWorkbook();

        try {
            Map<String, CellStyle> styles = ExcelUtil.createStyles(wb);

            //新建sheet页
            XSSFSheet sheet = wb.createSheet("总报告");
            //新建sheet页title
            Row titleRow = sheet.createRow(0);
            titleRow.setHeightInPoints(30);
            Cell titleCell = titleRow.createCell(0);
            titleCell.setCellStyle(styles.get("title"));
            titleCell.setCellValue(sstInfReport.getReportSource().equalsIgnoreCase(ReportSourceConstant.ONE_KEY_TEST) ? "一键压测报告" : "自动化测试报告");

            sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), titleRow.getRowNum(), 5));

            //构建excel的第一行
            List<CellDetail> cellDetails1 = new ArrayList<>();
            cellDetails1.add(new CellDetail(0, "总结果", styles.get("data2")));
            cellDetails1.add(new CellDetail(7, "说明", styles.get("data2")));
            ReportExcelUtils.builderExcelRow(sheet, 1, cellDetails1);

            //构建excel的第二行
            List<CellDetail> cellDetails2 = new ArrayList<>();
            cellDetails2.add(new CellDetail(0, "总请求数", styles.get("header")));
            cellDetails2.add(new CellDetail(1, "成功请求数", styles.get("header")));
            cellDetails2.add(new CellDetail(2, "失败请求数", styles.get("header")));
            cellDetails2.add(new CellDetail(3, "成功率", styles.get("header")));
            cellDetails2.add(new CellDetail(4, "耗时(s)", styles.get("header")));
            cellDetails2.add(new CellDetail(5, "平均接口耗时(ms)", styles.get("header")));
            cellDetails2.add(new CellDetail(7, "名称", styles.get("header")));
            cellDetails2.add(new CellDetail(8, sstInfReport.getReportName(), styles.get("data2")));
            ReportExcelUtils.builderExcelRow(sheet, 2, cellDetails2);

            //构建excel的第三行
            List<CellDetail> cellDetails3 = new ArrayList<>();
            cellDetails3.add(new CellDetail(0, sstInfReport.getCount() + "", styles.get("data2")));
            cellDetails3.add(new CellDetail(1, sstInfReport.getSuccessCount() + "", styles.get("data2")));
            cellDetails3.add(new CellDetail(2, sstInfReport.getFailCount() + "", styles.get("data2")));
            cellDetails3.add(new CellDetail(3, sstInfReport.getPercentage() + "%", styles.get("data2")));
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Long seconds = 0L;
            try {
                Date date1 = sdf.parse(sstInfReport.getRequestTime());
                Date date2 = sdf.parse(sstInfReport.getResponseTime());
                seconds = (date2.getTime() - date1.getTime());
            } catch (ParseException e) {
                throw new ServiceException(e.getMessage());
            }
            cellDetails3.add(new CellDetail(4, String.valueOf(new BigDecimal(seconds / 1000).setScale(2, ROUND_HALF_UP)), styles.get("data2")));
            cellDetails3.add(new CellDetail(5, String.valueOf( new BigDecimal(seconds / sstInfReport.getCount()).setScale(2, ROUND_HALF_UP)), styles.get("data2")));
            cellDetails3.add(new CellDetail(7, "环境地址", styles.get("header")));
            cellDetails3.add(new CellDetail(8, tsstEnvService.selectTsstEnvByEnvId(Long.valueOf(sstInfReport.getInfId())), styles.get("data2")));
            ReportExcelUtils.builderExcelRow(sheet, 3, cellDetails3);

            //构建excel的第四行
            List<CellDetail> cellDetails4 = new ArrayList<>();
            cellDetails4.add(new CellDetail(7,  "线程数", styles.get("header")));
            cellDetails4.add(new CellDetail(8, sstInfReport.getConcurrency() + "", styles.get("data2")));
            ReportExcelUtils.builderExcelRow(sheet, 4, cellDetails4);

            //构建excel的第五行
            List<CellDetail> cellDetails5 = new ArrayList<>();
            cellDetails5.add(new CellDetail(7,  "循环次数", styles.get("header")));
            cellDetails5.add(new CellDetail(8, sstInfReport.getRound() + "", styles.get("data2")));
            ReportExcelUtils.builderExcelRow(sheet, 5, cellDetails5);

            //构建excel的第六行
            List<CellDetail> cellDetails6 = new ArrayList<>();
            cellDetails6.add(new CellDetail(0,  "接口耗时", styles.get("data2")));
            ReportExcelUtils.builderExcelRow(sheet, 6, cellDetails6);

            //构建excel的第七行
            List<CellDetail> cellDetails7 = new ArrayList<>();
            cellDetails7.add(new CellDetail(0,  "接口名称", styles.get("header")));
            JSONArray objects = JSONArray.parseArray(sstInfReport.getAllInfInfo());
            List<SstInf> sstInfs = objects.toJavaList(SstInf.class);
            for (int i = 0; i < sstInfs.size(); i++) {
                cellDetails7.add(new CellDetail(i + 1, sstInfs.get(i).getInfName() + "", styles.get("data2")));
            }
            ReportExcelUtils.builderExcelRow(sheet, 7, cellDetails7);

            //构建excel的第八行
            List<CellDetail> cellDetails8 = new ArrayList<>();
            cellDetails8.add(new CellDetail(0,  "最大耗时(ms)", styles.get("header")));
            Map<Integer, List<Double>> timesMap = new HashMap<>();
            for (int i = 0; i < sstInfs.size(); i++) {
                timesMap.put(i, new ArrayList<Double>());
            }
            for (String allInfTime : sstInfReport.getAllInfTimes()) {
                String[] split = allInfTime.split(",");
                for (int i = 0; i < split.length; i++) {
                    timesMap.get(i % sstInfs.size()).add(Double.parseDouble(split[i]));
                }
            }
            for (int i = 0; i < timesMap.keySet().size(); i++) {
                cellDetails8.add(new CellDetail(i + 1,  Collections.max(timesMap.get(i)).toString(), styles.get("data2")));
            }
            ReportExcelUtils.builderExcelRow(sheet, 8, cellDetails8);
            //构建excel的第九行
            List<CellDetail> cellDetails9 = new ArrayList<>();
            cellDetails9.add(new CellDetail(0,  "最小耗时(ms)", styles.get("header")));
            for (int i = 0; i < timesMap.keySet().size(); i++) {
                cellDetails9.add(new CellDetail(i + 1,  Collections.min(timesMap.get(i)).toString(), styles.get("data2")));
            }
            ReportExcelUtils.builderExcelRow(sheet, 9, cellDetails9);

            //构建excel的第十行
            List<CellDetail> cellDetails10= new ArrayList<>();
            cellDetails10.add(new CellDetail(0,  "平均耗时(ms)", styles.get("header")));
            for (int i = 0; i < timesMap.keySet().size(); i++) {
                Double avg = timesMap.get(i).stream()
                        .mapToDouble(Double::doubleValue)
                        .average()
                        .orElse(0.0);
                cellDetails10.add(new CellDetail(i + 1, avg.toString(), styles.get("data2")));
            }
            ReportExcelUtils.builderExcelRow(sheet, 10, cellDetails10);
            //柱状图
            buildHistogram(sheet, sstInfReport);

            creatThreadSheet(wb, sstInfReport, styles);
            wb.write(response.getOutputStream());
        } catch (IOException e) {
            logger.error("导出IO异常：{0}"+e.getMessage());
        } catch (Exception e) {
            logger.error("构建导出对象错误");
        } finally {
            IOUtils.closeQuietly(wb);
        }
    }


    private void buildHistogram(XSSFSheet sheet, SstInfReport sstInfReport) {
        HistogramChart histogramChart = new HistogramChart();
        List<String> titles = new ArrayList<>();
        titles.add("线程耗时对比");
        titles.add("线程");
        titles.add("耗时(ms)");
        titles.add("耗时图例");
        histogramChart.setChartTitle(titles);
        histogramChart.setDataList(sstInfReport.getThreadTimes());
        String[] x = new String[histogramChart.getDataList().length];
        for (int i = 0; i < x.length; i++) {
            x[i] = "线程" + (i + 1);
        }
        histogramChart.setXAxisList(x);
        Integer[] position = new Integer[]{0,0,0,0,0,sheet.getLastRowNum() + 3,7,30};
        histogramChart.setPosition(position);
        ReportExcelUtils.createHistogram(sheet, histogramChart);
    }

    //构建每个线程的sheet页
    private void creatThreadSheet(XSSFWorkbook wb, SstInfReport sstInfReport, Map<String, CellStyle> styles) {
        for (int i = 0; i < sstInfReport.getAllInfTimes().size(); i++) {
            XSSFSheet sheet = wb.createSheet("线程" + (i + 1));
            List<CellDetail> head = new ArrayList<>();
            head.add(new CellDetail(0, "轮数", styles.get("header")));
            JSONArray objects = JSONArray.parseArray(sstInfReport.getAllInfInfo());
            List<SstInf> sstInfs = objects.toJavaList(SstInf.class);
            for (int j = 0; j < sstInfs.size(); j++) {
                head.add(new CellDetail(j + 1, sstInfs.get(j).getInfName() + "(ms)", styles.get("header")));
            }
            ReportExcelUtils.builderExcelRow(sheet, 0, head);
            String[] times = sstInfReport.getAllInfTimes().get(i).split(",");
            int round = times.length / sstInfs.size();
            for (int r = 0; r < round; r++) {
                List<CellDetail> row = new ArrayList<>();
                row.add(new CellDetail(0, "第" + (r + 1) + "轮", styles.get("data2")));
                for (int j = 0; j < sstInfs.size(); j++) {
                    row.add(new CellDetail(j + 1, times[sstInfs.size() * r + j], styles.get("data2")));
                }
                ReportExcelUtils.builderExcelRow(sheet, r + 1, row);
            }
        }
    }

    //构建excel方法
    private void exportExcel(HttpServletResponse response, SstInfReport sstInfReport) {
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");

        String fileName = sstInfReport.getReportName() + ".xlsx";
        response.setHeader("Content-Disposition", "filename=" + FileUtils.toUtf8String(fileName));

        XSSFWorkbook wb = new XSSFWorkbook();
        Map<String, CellStyle> styles = ExcelUtil.createStyles(wb);

        try {
            //新建sheet页
            XSSFSheet sheet = wb.createSheet("总报告");
            //新建sheet页title
            Row titleRow = sheet.createRow(0);
            Cell titleCell = titleRow.createCell(0);
            titleCell.setCellStyle(styles.get("title"));
            titleCell.setCellValue(sstInfReport.getReportSource().equalsIgnoreCase(ReportSourceConstant.ONE_KEY_TEST) ? "一键压测报告" : "自动化测试报告");
            sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), titleRow.getRowNum(), 5));

            List<CellDetail> cellDetails1 = new ArrayList<>();
            cellDetails1.add(new CellDetail(0, "总结果", styles.get("data2")));
            cellDetails1.add(new CellDetail(5, "说明", styles.get("data2")));
            ReportExcelUtils.builderExcelRow(sheet, 1, cellDetails1);

            //构建excel的第二行
            List<CellDetail> cellDetails2 = new ArrayList<>();
            cellDetails2.add(new CellDetail(0, "总请求数", styles.get("header")));
            cellDetails2.add(new CellDetail(1, "成功请求数", styles.get("header")));
            cellDetails2.add(new CellDetail(2, "失败请求数", styles.get("header")));
            cellDetails2.add(new CellDetail(3, "成功率", styles.get("header")));
            cellDetails2.add(new CellDetail(5, "名称", styles.get("header")));
            cellDetails2.add(new CellDetail(6, sstInfReport.getReportName(), styles.get("data2")));
            ReportExcelUtils.builderExcelRow(sheet, 2, cellDetails2);

            //构建excel的第三行
            List<CellDetail> cellDetails3 = new ArrayList<>();
            cellDetails3.add(new CellDetail(0, sstInfReport.getCount() + "", styles.get("data2")));
            cellDetails3.add(new CellDetail(1, sstInfReport.getSuccessCount() + "", styles.get("data2")));
            cellDetails3.add(new CellDetail(2, sstInfReport.getFailCount() + "", styles.get("data2")));
            cellDetails3.add(new CellDetail(3, NumberUtil.round(NumberUtil.div(sstInfReport.getSuccessCount(), sstInfReport.getCount()), 4).doubleValue() * 100 + "%", styles.get("data2")));
            cellDetails3.add(new CellDetail(5, "环境", styles.get("header")));
            cellDetails3.add(new CellDetail(6, tsstEnvService.selectTsstEnvByEnvId(Long.valueOf(sstInfReport.getInfEnv())), styles.get("data2")));
            ReportExcelUtils.builderExcelRow(sheet, 3, cellDetails3);

            //构建excel的第四行
            List<CellDetail> cellDetails4 = new ArrayList<>();
            cellDetails4.add(new CellDetail(5, "方法", styles.get("header")));
            cellDetails4.add(new CellDetail(6, sstInfReport.getInfMethod(), styles.get("data2")));
            ReportExcelUtils.builderExcelRow(sheet, 4, cellDetails4);

            //构建excel的第五行
            List<CellDetail> cellDetails5 = new ArrayList<>();
            cellDetails5.add(new CellDetail(0, "主要指标", styles.get("data2")));
            cellDetails5.add(new CellDetail(5, "URL", styles.get("header")));
            cellDetails5.add(new CellDetail(6, sstInfReport.getInfUrl(), styles.get("data2")));
            ReportExcelUtils.builderExcelRow(sheet, 5, cellDetails5);

            //构建excel的第六行
            List<CellDetail> cellDetails6 = new ArrayList<>();
            cellDetails6.add(new CellDetail(0, "总耗时(ms)", styles.get("header")));
            cellDetails6.add(new CellDetail(1, "单笔最大耗时(ms)", styles.get("header")));
            cellDetails6.add(new CellDetail(2, "单笔最小耗时(ms)", styles.get("header")));
            cellDetails6.add(new CellDetail(3, "平均耗时(ms)", styles.get("header")));
            cellDetails6.add(new CellDetail(5, "线程数", styles.get("header")));
            cellDetails6.add(new CellDetail(6, sstInfReport.getConcurrency() + "", styles.get("data2")));
            ReportExcelUtils.builderExcelRow(sheet, 6, cellDetails6);

            //构建excel的第七行
            List<CellDetail> cellDetails7 = new ArrayList<>();
            cellDetails7.add(new CellDetail(0, sstInfReport.getConsumeTime() + "", styles.get("data2")));
            cellDetails7.add(new CellDetail(1, sstInfReport.getMaxConsumeTime() + "", styles.get("data2")));
            cellDetails7.add(new CellDetail(2, sstInfReport.getMinConsumeTime() + "", styles.get("data2")));
            cellDetails7.add(new CellDetail(3, sstInfReport.getAvgConsumeTime() + "", styles.get("data2")));
            cellDetails7.add(new CellDetail(5, "循环数", styles.get("header")));
            cellDetails7.add(new CellDetail(6, sstInfReport.getRound() + "", styles.get("data2")));
            ReportExcelUtils.builderExcelRow(sheet, 7, cellDetails7);

            //柱状图
            buildHistogram(sheet, sstInfReport);

            creatThreadSheet(wb, sstInfReport, styles);

            wb.write(response.getOutputStream());

        } catch (Exception e) {
            logger.error("导出Excel报告异常{}", e.getMessage());
        } finally {
            IOUtils.closeQuietly(wb);
        }


    }


}
